Baklava

What is a sprite?

Sprites are graphical objects visible on the computer screen. Most sprites are also moving objects. Sprites are useful in the implementation of active imagemaps, puzzles, simulations, and games, among other possibilities. Sprites are aware of collisions with other sprites, as well as interactions with the mouse, the keyboard, and the edges of the playfield. Sprites always reside in playfields, which must be constructed first.

Other capabilities of sprites

Baklava sprites can be told what direction to move in and how fast to travel (in pixels per second), without further intervention from the programmer, until something "interesting" such as a collision takes place.

In addition, each sprite knows what "level" it is on. Sprites at higher levels are drawn on top. This allows "foreground" sprites to move across "background" sprites. It is also possible to specify that certain sprites never collide with others.

If the setImage method is used to assign an image that was loaded from a transparent GIF, then baklava will take this into account in determining collisions. This allows non-rectangular sprites to interact gracefully.

Extending Sprites: Yes, You Will Need To

To do truly interesting things with sprites, you will need to "subclass" them. This allows you to write your own class that has all of the built-in behavior of a sprite, plus the additional "smarts" necessary for your own application.

Receiving Events: Pick Up the Courtesy Phone...

Methods such as collisionWith, which do nothing in the "raw" Sprite class, are invoked by the playfield to let you know when one sprite collides with another sprite or with the edges of the playfield. You can replace these methods with useful versions in your subclass of Sprite.

Also, The very important timer method is invoked when a timer set by a call to setTimer or setTimerAll expires. Replace this method to carry out particular actions at particular times, or when notified by another sprite.

Other events that are reported to sprites are normally handled well by the built-in sprite code, but you can add additional behavior. Methods such as onStep and onGoodbye can optionally be replaced to move in more complex ways, or to carry out extra steps when a sprite is removed from a playfield.

About Sprites and Threads...

Implementing a separate thread for each sprite is not recommended. Instead, subclass the Sprite class to respond to various events, and use the setTimer method to carry out particular actions at particular times. Baklava takes great care to deliver timers in the correct order and to synchronize the delivery of timers and events in a safe and reliable manner.

Example: Shy Sprites

This applet contains a group of sprites which are uncomfortable with each other, and with you, for that matter. When they come in contact with one another or the mouse, they change direction and run away quickly. This is done by subclassing Sprite to add new versions of the collisionWith and mouseEnter methods.
import baklava.*;

// A private class implementing the shy sprites.
class Shy extends Sprite {

	// A timer ID we'll use later. "final" so java
	// will know it's just a constant and not
	// reserve any real space for it.
	final int timerSlowDown = 0;

	public Shy(Playfield p, Image image)
	{
		// Call the Sprite constructor. This is required.
		super(p);
		// Now set the image.
		setImage(image);
		// Pick a random location safely inside the playfield.
		// Math.random() returns a value between 0 and 1.
		int x = (int) (Math.random() * (p.getWidth() - getWidth()));
		int y = (int) (Math.random() * (p.getHeight() - getHeight()));
		// Set the location.
		setX(x);
		setY(y);
		// Set a direction.
		setDirection((int) (Math.random() * 360));
		// Set a speed.
		setSpeed(30);
		// Just bounce at the edges of the playfield.
		setEdgeHandling(edgeBounce);
	}	
	void collisionWith(Sprite s)
	{
		// We collided with something. Run awaaay!

		// Find the direction to the thing we collided with.

		int direction = getPlayfield().angleOfVector(
			getX(), getY(), s.getX(), s.getY());

		// Now go the other way...	
		direction += 180;
		setDirection(direction);	

		// And speed up
		setSpeed(60);

		// Set a timer to slow down after 2 seconds.
		setTimer(2000, timerSlowDown);
	}
	void mouseEnter(Event evt, int x, int y)
	{
		// The mouse entered the sprite. Run away.
		setDirection((int) (Math.random() * 360));
		setSpeed(60);
		// Set a timer to slow down after 2 seconds.
		setTimer(2000, timerSlowDown);
	}
	void timer(int timerId)
	{
		if (timerId == timerSlowDown) {
			// Relax.
			setSpeed(30);
		}
	}
}

public class ShyApplet extends Applet {
	Playfield p;
	// CHANGE THIS, of course, to a directory where you keep
	// suitable GIFs.
	String base = "http://www.mysite.com/graphics/";
	public void init() {
		// Use no layout manager
		setLayout(null);

		// Create the playfield
		p = new Playfield(this, bounds().width, bounds().height); 

		// Fetch an image
		Image wanderer = p.getImage(base + "wanderer.gif");

		// Construct the Shy objects. 
		int i;
		for (i = 0; (i < 5); i++) {
			new Shy(p, wanderer);		
		}
		// Now start movement in the playfield
		p.start();
	
		// Now display the playfield
		add(p);
	}	
	public void stop() {
		p.suspend();
	}
	public void start() {
		p.resume();
	}
	public void destroy() {
		p.stop();
	}
};

baklava.Sprite Method Reference

This section provides a reference guide to the publicly accessible methods (functions) available in the baklava.Sprite class.
Sprite(Playfield playfieldArg)
This is the constructor method. The constructor accepts a playfield as an argument. You must construct a playfield first before constructing a sprite. If you subclass Sprite, which is very common, you will need to invoke this constructor at the start of your subclass constructor. Example:
class Tree extends Sprite {
	Tree(Playfield p, int x, int y) {
		// Call the Sprite constructor
		super(p);
		// Now do other things...
	}
}
final void goodbye()
The goodbye method should be invoked when a sprite is no longer wanted. In response, baklava will call the onGoodbye method, and then release various resources associated with the sprite. Note that goodbye itself cannot be replaced by a subclass of sprite, but onGoodbye is provied for this purpose.
void onGoodbye()
The onGoodbye method is invoked by baklava when the goodbye method is invoked, and before the resources associated with the sprite are released. The default implementation of onGoodbye does nothing. This method is provided as an event "hook" for the programmer to replace in subclasses of sprite. For instance, you might wish to invoke the goodbye method on other sprites associated with this sprite.
final void setSpeed(int s)
The setSpeed method sets the speed of movement of the sprite, in pixels per second. Together with the setDirection, setDirectionToward and setTarget methods, this provides a simple way to animate sprites without a great deal of effort on your part.
final int getSpeed(int s)
The getSpeed method returns the speed of movement of the sprite, in pixels per second.
final void setLevel(int l)
It is often desirable for one sprite to be thought of as "on top" of another. For example, many applets include one "background" sprite which should appear beneath the rest. In baklava, this is accomplished by calling the setLevel method to indicate the "stacking order" of the sprites. The greater the value of l, the higher the sprite appears in the stacking order. By default, all sprites have a level of zero (0), so a background can be created by invoking setLevel on a large sprite with a level of -1. To greatly improve performance, it is also important to call setBackground on sprites that are considered to be part of the background.
final int getLevel()
The getLevel method returns the "level" of this sprite in the stacking order. The highest-numbered sprites appear on top when sprites overlap.
final void setX(int x)
The setX method sets the location of the sprite on the X axis, in pixels measured from left to right. The leftmost pixel is at coordinate zero (0).
final int getX()
The getX method returns the location of the sprite on the X axis, in pixels measured from left to right. The leftmost pixel is at coordinate zero (0).
final void setY(int x)
The setY method sets the location of the sprite on the Y axis, in pixels measured from top to bottom. The topmost pixel is at coordinate zero (0).
final int getY()
The getY method returns the location of the sprite on the Y axis, in pixels measured from top to bottom. The topmost pixel is at coordinate zero (0).
final void setTile(Image image, int w, int h)
The setTile method sets a new appearance for the sprite. The sprite will be of the size indicated by the w (width) and h (height) arguments, and the image will be "tiled" over and over throughout the sprite. For an easy way to fetch images via the web, see the Playfield.getImage method. The use of that method is strongly recommended. setTile is commonly used to create background sprites; for more on creating backgrounds, see the setBackground and setLevel methods.
final void setImage(Image image)
The setImage method sets a new appearance for the sprite. The size of the sprite will match the size of the image. For an easy way to fetch images via the web, see the Playfield.getImage method. The use of that method is strongly recommended.
final Image getImage()
The getImage method returns the current image being displayed by the sprite. Note that if the image was set using the setTile method, the image returned by getImage will be a large image containing many repetitions of the original tile, rather than just not just the tile.
final void setWidth(int w)
The setWidth method sets the width of the sprite, in pixels. This is most useful when the programmer also replaces the paint method to draw something other than or in addition to an ordinary image. Note that the width will be changed again if the setImage method is called later.
final int getWidth()
The getWidth method returns the width of the sprite, in pixels. The value returned by this method is not meaningful until setImage or setTile has been called.
final void setHeight(int h)
The setHeight method sets the height of the sprite, in pixels. This is most useful when the programmer also replaces the paint method to draw something other than or in addition to an ordinary image. Note that the height will be changed again if the setImage method is called later.
final int getHeight()
The getHeight method returns the height of the sprite, in pixels. The value returned by this method is not meaningful until setImage or setTile has been called.
final void setDirection(int direction)
The setDirection method sets the direction (angle), in degrees, in which the sprite will move. Acceptable values range from 0 to 360. Larger values are rounded down by dividing by 360 and taking the remainder. Zero degrees points directly to the right. Note that, because the Y axis ranges from top to bottom, 90 degrees points straight down. See also setDirectionToward for a way to aim the sprite toward a specific point or object, setTarget for a way to send an sprite toward a specific point and stop moving when it arrives, and setSpeed for a way to set the sprite in motion. Sprite motion does not actually begin until the Playfield.start method has been invoked.
final void setDirectionToward(int x, int y) OR final void setDirectionToward(Sprite s)
The setDirectionToward method computes the angle from the sprite's current location to the point in the playfield or other sprite indicated, and sets the sprite's direction of movement to match that angle. See also setTarget for a way to "aim" a sprite toward a specific point and stop moving when it arrives, and setSpeed for a way to set the sprite in motion. Sprite motion does not actually begin until the Playfield.start method has been invoked.
final void setTarget(int x, int y)
The setTarget method computes the angle from the sprite's current location to the point indicated, and sets the sprite's direction of movement to match that angle. When the sprite arrives at the target, it automatically stops moving, and the onArrival method is called. See also setDirectionToward for a way to "aim" a sprite toward a specific point or sprite without automatically stopping upon arrival, and setSpeed for a way to set the sprite in motion. Sprite motion does not actually begin until the Playfield.start method has been invoked.
public void onArrival()
The onArrival method is called when a sprite has arrived at a target point set with the setTarget method. By default, this method does nothing. Provide your own version in your subclass of sprite to carry out interesting actions when sprites arrive at their destinations.
final int getDirection()
The getDirection method returns the direction, in degrees, in which the sprite is currently moving.
void setEdgeHandling(int edgeHandlingArg)
The setEdgeHandling method determines how the sprite should behave when it contacts the edge of the playfield. Several simple behaviors are available. If the argument to setEdgeHandling is Sprite.edgeSolid, sprites will simply stop moving at the edge of the playfield. If the argument is set to Sprite.edgeBounce, sprites will rebound off the edge of the playfield. If the argument is set to Sprite.edgeWrap, sprites will pass through the edge of the playfield and reappear on the far side. Solid edges are the default. To implement other behaviors at the edge, replace the collisionEdge method with a new implementation in a subclass of Sprite.
void mouseDown(Event evt, int x, int y)
The mouseDown method is invoked by the playfield when a mouse button is pressed down within this sprite. The x and y arguments are relative to the upper left corner of the sprite. By default, this method does nothing. To react to the mouse, replace this method in your subclass of sprite. The event argument is identical to the event passed to the java.awt.Component.mouseDown method of the playfield by the Java window toolkit.
void mouseUp(Event evt, int x, int y)
The mouseUp method is invoked by the playfield when a mouse button is released within this sprite. The x and y arguments are relative to the upper left corner of the sprite. By default, this method does nothing. To react to the mouse, replace this method in your subclass of sprite. The event argument is identical to the event passed to the java.awt.Component.mouseUp method of the playfield by the Java window toolkit.
void mouseMove(Event evt, int x, int y)
The mouseMove method is invoked by the playfield when the mouse is moved within this sprite. The x and y arguments are relative to the upper left corner of the sprite. By default, this method does nothing. To react to the mouse, replace this method in your subclass of sprite. The event argument is identical to the event passed to the java.awt.Component.mouseMove method of the playfield by the Java window toolkit.
void mouseMove(Event evt, int x, int y)
The mouseMove method is invoked by the playfield when the mouse is moved within this sprite. The x and y arguments are relative to the upper left corner of the sprite. By default, this method does nothing. To react to the mouse, replace this method in your subclass of sprite. The event argument is identical to the event passed to the java.awt.Component.mouseMove method of the playfield by the Java window toolkit.
void mouseDrag(Event evt, int x, int y)
The mouseDrag method is invoked by the playfield when the mouse is dragged (moved with a mouse button down) within this sprite. The x and y arguments are relative to the upper left corner of the sprite. By default, this method does nothing. To react to the mouse, replace this method in your subclass of sprite. The event argument is identical to the event passed to the java.awt.Component.mouseDrag method of the playfield by the Java window toolkit.
void mouseEnter(Event evt, int x, int y)
The mouseEnter method is invoked by the playfield when the mouse enters this specific sprite. The x and y arguments are relative to the upper left corner of the sprite. By default, this method does nothing. To react to the mouse, replace this method in your subclass of sprite. Since sprites are not considered to be windows by the Java window toolkit itself, the event argument is that which was passed to the java.awt.Component.mouseMove (not mouseEnter) method of the playfield.
void mouseExit(Event evt, int x, int y)
The mouseExit method is invoked by the playfield when the mouse enters this specific sprite. The x and y arguments are relative to the upper left corner of the sprite. By default, this method does nothing. To react to the mouse, replace this method in your subclass of sprite. Since sprites are not considered to be windows by the Java window toolkit itself, the event argument is that which was passed to the java.awt.Component.mouseMove (not mouseExit) method of the playfield.
void keyDown(Event evt, int key)
The keyDown method is invoked by the playfield when a key is pressed down anywhere in the playfield. The x and y arguments are relative to the upper left corner of the sprite. By default, this method does nothing. To react to the keyboard, replace this method in your subclass of sprite. The event argument is identical to the event passed to the java.awt.Component.keyDown method of the playfield by the Java window toolkit. Note that, for consistency with the AWT, the key argument is an integer, not a char. You can compare it to a character by casting it to a char. Example:
	// ... Inside a subclass of Sprite ... 
	public void keyDown(Event evt, int key)
	{
		if (((char) key) == ' ') {
			// Dismiss this sprite when the space bar is pressed
			goodbye();
		}
	}
void keyUp(Event evt, int key)
The keyUp method is invoked by the playfield when a key is released anywhere in the playfield. The x and y arguments are relative to the upper left corner of the sprite. By default, this method does nothing. To react to the keyboard, replace this method in your subclass of sprite. The event argument is identical to the event passed to the java.awt.Component.keyUp method of the playfield by the Java window toolkit. Note that, for consistency with the AWT, the key argument is an integer, not a char. You can compare it to a character by casting it to a char. Example:
	// ... Inside a subclass of Sprite ... 
	public void keyUp(Event evt, int key)
	{
		if (((char) key) == ' ') {
			// Dismiss this sprite when the space bar is released
			goodbye();
		}
	}
void collisionEdge(int edge)
The collisionEdge method is invoked by the playfield when the sprite collides with one of the four edges of the playfield. By default, collisionEdge carries out the behavior set by the setEdgeHandling method. If you need behavior that method cannot provide, replace collisionEdge in your subclass of sprite. The edge argument will be set to Sprite.edgeTop, Sprite.edgeBottom, Sprite.edgeLeft, or Sprite.edgeRight.
void collisionWith(Sprite s)
The collisionWith method is invoked by the playfield when a sprite collides with another sprite. The method is invoked in both directions; that is, when sprites a and b collide, both a.collisionWith(b) and b.collisionWith(a) will be called by the playfield. Collisions take transparent pixels into account if the sprite's image was loaded from a transparent GIF; this allows non-rectangular objects to behave in a realistic way. By default, this method does nothing. Replace it in a subclass of sprite in order to react to collisions. Note that, if you use several subclasses of sprite, Java's Object.getClass() and Class.forName() methods become very useful in determining what type of sprite has collided with this sprite. Example:
// ... inside a subclass of sprite ...
public void collisionWith(Sprite s)
{
	if (s.getClass() == Class.forName("Scone")) {
		// Let that scone know it's been hit.
		// We know this object is a Scone, so
		// we can now safely cast its type.
		Scone scone = (Scone) s;
		scone.hit();
	}
}
void onStep(int elapsed)
Normally, baklava moves sprites according to their direction (as set by setDirection) and their speed (as set by setSpeed). Occasionally, the programmer moves a sprite more abruptly using the setX and setY methods. However, there are times when these methods are not sufficient. If your sprite has very complex behavior, you may wish to replace the onStep method in your subclass of sprite in order to move the sprite independently at every step in baklava's simulation of sprite movement. The elapsed argument indicates how many milliseconds (thousandths of a second) of time have elapsed since the previous step in the simulation; this argument varies considerably. In most cases, it is preferable to use the setDirection, setDirectionToward, and setSpeed methods for movement, and the setTimer method for special actions at specific times.
void paint(Graphics g)
Normally, baklava will paint sprites in the playfield for you, displaying the appropriate image as requested using the setImage and setTile methods. In unusual cases, you may wish to override this behavior and paint the sprite yourself, by replacing the paint method in a subclass of Sprite. If you want to draw additional information in addition to the normal sprite image, be sure to call the superclass version of the paint method from your replacement, as shown in the following example:
// ... in a subclass of Sprite ...
public void paint(Graphics g)
{
	// First call the original paint method of Sprite
	// to paint the image.
	super.paint(g);

	// Now draw a blue rectangle around the image
	// if a flag is set.
	if (highlighted) {	
		g.setColor(java.awt.Color.blue);
		// NOTE: coordinates are relative to the playfield,
		// not the sprite!
		g.drawRectangle(getX(), getY(), getWidth(), getHeight());
	}
}
void setTimer(int delay, int id)
This method is used to set a timer. After delay milliseconds (thousandths of a second), the timer method will be invoked with the same id argument. This mechanism is the best way to carry out specific actions at specific times. Also, settimg timers with a delay of zero on other sprites is a useful way to communicate between sprites. See also Playfield.setTimerAll for a way to set a timer for all sprites, or for all sprites of a specific subclass. See Playfield.setGlobalTimer for a way to set a "global" timer that is not associated with a particular sprite.
void timer(int id)
This method is invoked by the playfield when a timer set for this sprite has expired. By default, this method does nothing. Replace this method in your subclass of Sprite to respond to timers. Timers are an important part of the baklava system and are very useful as a way to carry out events at particular times, as well as to communicate between sprites. See the setTimer, Playfield.setTimerAll and Playfield.setGlobalTimer methods for more information on this subject.
final boolean getBackground()
This method returns true if the background flag is set, false otherwise. "Background" sprites use fewer resources, and never collide with other objects or receive events such as mouseDown and keyDown.
final void setBackground(boolean flag)
This method sets the background flag for this sprite. "Background" sprites use fewer resources, and never collide with other objects or receive events such as mouseDown and keyDown. For best results, be sure to call the setBackground method before calling setImage or setTile.
final boolean getRectangular()
This method returns true if the rectangular flag is set, false otherwise. "Rectangular" sprites use fewer resources, and are considered perfect rectangles for the purpose of determining collisions. (Sprites without an image are always considered rectangular.)
final void setRectangular(boolean flag)
This method sets the rectangular flag for this sprite. "Rectangular" sprites use fewer resources, and are considered perfect rectangles for the purpose of determining collisions. For best results, be sure to call the setRectangular method before calling setImage or setTile. (Sprites without an image are always considered rectangular.)

Up to Index
Copyright 1996 by Boutell.Com, Inc.